import socket
import json
import traceback
import threading
from utils import memory
from utils import model
import time


class Server:
    def __init__(self, addr, route):
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._socket.bind(addr)
        self.route = route

    def launch(self):
        self._socket.listen()
        while True:
            try:
                conn, addr = self._socket.accept()
                session = Session(self, conn, addr)
                td = threading.Thread(target=session.launch)
                td.start()
            except KeyboardInterrupt:
                print('KeyboardInterrupt detected. Server stop.')
                break
            except Exception as e:
                traceback.print_exception(e)
        self._socket.close()


class Session:
    def __init__(self, server, conn, addr):
        assert isinstance(server, Server)
        assert isinstance(conn, socket.SocketType)
        self._server = server
        self._conn = conn
        self._addr = addr

    def launch(self):
        msg = self._conn.recv(1000000)
        if len(msg) == 0:
            print('Client close TCP connection.')
            return None
        txt = msg.decode('utf-8')
        obj = json.loads(txt)
        print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), ' ', obj['url'])
        api = self._server.route[obj['url']]()
        if isinstance(api, UpgradeInterface):
            api.set_client(self._conn, self._addr)
            api.handle_wrapper(obj)
        else:
            res = api.handle_wrapper(obj)
            self._conn.send(json.dumps(res).encode('utf-8'))
            self._conn.close()


class Interface:
    def __init__(self):
        self.req = None
        self.req_body = None
        self.token = None
        self.user = None

    def handle(self, req):
        pass

    def handle_wrapper(self, req):
        try:
            self.req = req
            self.req_body = req['body']
            if req['url'] not in ['/user/login', '/user/register']:
                if req['token'] in memory.token2account:
                    self.token = req['token']
                    self.user = memory.users[memory.token2account[req['token']]]
                else:
                    return self.failed_respond('INVALID_TOKEN', '无效 token ，客户端没有登录态')
            return self.handle(req['body'])
        except Exception as e:
            traceback.print_exception(e)
            return self.failed_respond('UNKNOWN', '')

    def success_respond(self, body):
        return {
            'code': 'OK',
            'message': '',
            'body': body
        }

    def failed_respond(self, code, message='', body=None):
        if body is None:
            body = {}
        return {
            'code': code,
            'message': message,
            'body': body
        }


class UpgradeInterface(Interface):

    def __init__(self):
        Interface.__init__(self)
        self._conn = None
        self._addr = None

    def set_client(self, conn, addr):
        self._conn = conn
        self._addr = addr
